//! Import declaration parser (flexible syntax)

use super::super::Parser;
use crate::ast::decl::*;
use crate::error::{CompilerError, Result};
use crate::lexer::token::Token;

/// Parse import declaration
///
/// Supported syntax:
/// 1. ES6-style with braces (multi-line):
///    - `import { foo, bar } from "./module.nr"`
///    - `import { foo as f } from "./module.nr"`
///
/// 2. Simplified without braces (single-line):
///    - `import add, subtract from "./math.nr"`
///    - `import add as plus from "./math.nr"`
///
/// 3. Python-style (from first):
///    - `from "./math.nr" import add, subtract`
///    - `from "./math.nr" import add as plus`
///
/// 4. Namespace import:
///    - `import * as math from "./math.nr"`
///
/// 5. Default import:
///    - `import math from "./math.nr"`
pub fn parse_import_declaration(parser: &mut Parser) -> Result<ImportDecl> {
    let location = parser.current_location();

    // Check if this is Python-style: from "..." import ...
    if parser.check(&Token::From) {
        parse_from_import(parser, location)
    } else {
        // ES6-style: import ... from "..."
        parse_import_from(parser, location)
    }
}

/// Parse Python-style import: from "..." import ...
fn parse_from_import(parser: &mut Parser, location: crate::error::Location) -> Result<ImportDecl> {
    parser.expect(&Token::From)?;

    // Parse import path (string literal)
    let path = if let Some(Token::String(path_str)) = parser.peek().map(|t| &t.token) {
        let path = path_str.clone();
        parser.advance()?;
        path
    } else {
        return Err(CompilerError::syntax(
            parser.current_location().line,
            parser.current_location().column,
            "Expected string literal for import path",
        ));
    };

    parser.expect(&Token::Import)?;

    // Check for namespace import: from "..." import * as foo
    if parser.consume(&Token::Star)? {
        parser.expect(&Token::As)?;
        let namespace_name = if let Some(Token::Identifier(name)) = parser.peek().map(|t| &t.token)
        {
            let name = name.clone();
            parser.advance()?;
            name
        } else {
            return Err(CompilerError::syntax(
                parser.current_location().line,
                parser.current_location().column,
                "Expected identifier after 'as'",
            ));
        };

        parser.consume(&Token::Semicolon)?;

        return Ok(ImportDecl {
            path,
            default_import: None,
            named_imports: vec![],
            namespace_import: Some(namespace_name),
            location,
        });
    }

    // Parse named imports (with or without braces)
    let named_imports = if parser.consume(&Token::LeftBrace)? {
        // With braces: from "..." import { a, b }
        let imports = parse_named_imports_list(parser)?;
        parser.expect(&Token::RightBrace)?;
        imports
    } else {
        // Without braces: from "..." import a, b
        parse_named_imports_list(parser)?
    };

    parser.consume(&Token::Semicolon)?;

    Ok(ImportDecl {
        path,
        default_import: None,
        named_imports,
        namespace_import: None,
        location,
    })
}

/// Parse ES6-style import: import ... from "..."
fn parse_import_from(parser: &mut Parser, location: crate::error::Location) -> Result<ImportDecl> {
    parser.expect(&Token::Import)?;

    let mut default_import: Option<String> = None;
    let mut named_imports: Vec<ImportItem> = Vec::new();
    let mut namespace_import: Option<String> = None;

    // Check for namespace import: import * as foo
    if parser.consume(&Token::Star)? {
        parser.expect(&Token::As)?;

        if let Some(Token::Identifier(name)) = parser.peek().map(|t| &t.token) {
            namespace_import = Some(name.clone());
            parser.advance()?;
        } else {
            return Err(CompilerError::syntax(
                parser.current_location().line,
                parser.current_location().column,
                "Expected identifier after 'as'",
            ));
        }
    }
    // Check for braced imports: import { ... }
    else if parser.consume(&Token::LeftBrace)? {
        named_imports = parse_named_imports_list(parser)?;
        parser.expect(&Token::RightBrace)?;
    }
    // Check for default import or named imports without braces
    else if let Some(Token::Identifier(name)) = parser.peek().map(|t| &t.token) {
        let first_name = name.clone();
        parser.advance()?;

        // Check what comes after the identifier
        if parser.consume(&Token::Comma)? {
            // Could be:
            // 1. import foo, { bar } from "..." (mixed with braces)
            // 2. import foo, bar from "..." (multiple without braces)

            if parser.consume(&Token::LeftBrace)? {
                // Mixed: import foo, { bar, baz }
                default_import = Some(first_name);
                named_imports = parse_named_imports_list(parser)?;
                parser.expect(&Token::RightBrace)?;
            } else {
                // Multiple without braces: import foo, bar, baz
                named_imports.push(ImportItem {
                    name: first_name,
                    alias: None,
                });
                named_imports.extend(parse_named_imports_list(parser)?);
            }
        } else if parser.check(&Token::As) {
            // Import with alias: import foo as bar (possibly followed by comma)
            parser.expect(&Token::As)?;
            let alias = if let Some(Token::Identifier(alias_name)) = parser.peek().map(|t| &t.token)
            {
                let name = alias_name.clone();
                parser.advance()?;
                name
            } else {
                return Err(CompilerError::syntax(
                    parser.current_location().line,
                    parser.current_location().column,
                    "Expected identifier after 'as'",
                ));
            };

            named_imports.push(ImportItem {
                name: first_name,
                alias: Some(alias),
            });

            // Check if there are more imports: import a as x, b as y
            if parser.consume(&Token::Comma)? {
                named_imports.extend(parse_named_imports_list(parser)?);
            }
        } else if parser.check(&Token::From) {
            // Default import: import foo from "..."
            default_import = Some(first_name);
        } else {
            return Err(CompilerError::syntax(
                parser.current_location().line,
                parser.current_location().column,
                "Expected 'from', ',', or 'as' after identifier",
            ));
        }
    } else {
        return Err(CompilerError::syntax(
            parser.current_location().line,
            parser.current_location().column,
            "Expected import specifier (identifier, '{', or '*')",
        ));
    }

    // Expect 'from' keyword
    parser.expect(&Token::From)?;

    // Parse import path (string literal)
    let path = if let Some(Token::String(path_str)) = parser.peek().map(|t| &t.token) {
        let path = path_str.clone();
        parser.advance()?;
        path
    } else {
        return Err(CompilerError::syntax(
            parser.current_location().line,
            parser.current_location().column,
            "Expected string literal for import path (e.g., \"./module.nr\", \"@project/foo.nr\")",
        ));
    };

    // Optional semicolon
    parser.consume(&Token::Semicolon)?;

    Ok(ImportDecl {
        path,
        default_import,
        named_imports,
        namespace_import,
        location,
    })
}

/// Parse list of named imports: foo, bar as baz, qux
/// This works both inside braces and without braces
fn parse_named_imports_list(parser: &mut Parser) -> Result<Vec<ImportItem>> {
    let mut items = Vec::new();

    loop {
        // Check for end of imports
        if parser.check(&Token::RightBrace)
            || parser.check(&Token::From)
            || parser.check(&Token::Semicolon)
        {
            break;
        }

        // Parse import name
        if let Some(Token::Identifier(name)) = parser.peek().map(|t| &t.token) {
            let import_name = name.clone();
            parser.advance()?;

            // Check for alias: foo as bar
            let alias = if parser.consume(&Token::As)? {
                if let Some(Token::Identifier(alias_name)) = parser.peek().map(|t| &t.token) {
                    let alias = alias_name.clone();
                    parser.advance()?;
                    Some(alias)
                } else {
                    return Err(CompilerError::syntax(
                        parser.current_location().line,
                        parser.current_location().column,
                        "Expected identifier after 'as'",
                    ));
                }
            } else {
                None
            };

            items.push(ImportItem {
                name: import_name,
                alias,
            });

            // Check for comma (more imports) or end
            if !parser.consume(&Token::Comma)? {
                break;
            }
        } else {
            break;
        }
    }

    Ok(items)
}

#[cfg(test)]
mod tests {
    use super::*;
    use crate::parser::Parser;

    fn parse_import(input: &str) -> Result<ImportDecl> {
        let mut parser = Parser::new(input.to_string(), None);
        parser.advance()?;
        parse_import_declaration(&mut parser)
    }

    // ES6-style tests with braces
    #[test]
    fn test_named_import_with_braces() {
        let result = parse_import(r#"import { add, subtract } from "./math.nr""#);
        assert!(result.is_ok());
        let import = result.unwrap();
        assert_eq!(import.path, "./math.nr");
        assert_eq!(import.named_imports.len(), 2);
        assert_eq!(import.named_imports[0].name, "add");
        assert_eq!(import.named_imports[1].name, "subtract");
    }

    // Simplified syntax without braces
    #[test]
    fn test_named_import_without_braces() {
        let result = parse_import(r#"import add, subtract from "./math.nr""#);
        assert!(result.is_ok());
        let import = result.unwrap();
        assert_eq!(import.path, "./math.nr");
        assert_eq!(import.named_imports.len(), 2);
        assert_eq!(import.named_imports[0].name, "add");
        assert_eq!(import.named_imports[1].name, "subtract");
    }

    #[test]
    fn test_single_import_with_alias() {
        let result = parse_import(r#"import add as plus from "./math.nr""#);
        assert!(result.is_ok());
        let import = result.unwrap();
        assert_eq!(import.named_imports.len(), 1);
        assert_eq!(import.named_imports[0].name, "add");
        assert_eq!(import.named_imports[0].alias, Some("plus".to_string()));
    }

    // Python-style tests
    #[test]
    fn test_from_import() {
        let result = parse_import(r#"from "./math.nr" import add, subtract"#);
        assert!(result.is_ok());
        let import = result.unwrap();
        assert_eq!(import.path, "./math.nr");
        assert_eq!(import.named_imports.len(), 2);
        assert_eq!(import.named_imports[0].name, "add");
    }

    #[test]
    fn test_from_import_with_alias() {
        let result = parse_import(r#"from "./math.nr" import add as plus"#);
        assert!(result.is_ok());
        let import = result.unwrap();
        assert_eq!(import.named_imports.len(), 1);
        assert_eq!(import.named_imports[0].alias, Some("plus".to_string()));
    }

    #[test]
    fn test_from_import_with_braces() {
        let result = parse_import(r#"from "./math.nr" import { add, subtract }"#);
        assert!(result.is_ok());
        let import = result.unwrap();
        assert_eq!(import.named_imports.len(), 2);
    }

    // Other syntax tests
    #[test]
    fn test_namespace_import() {
        let result = parse_import(r#"import * as math from "./math.nr""#);
        assert!(result.is_ok());
        let import = result.unwrap();
        assert_eq!(import.path, "./math.nr");
        assert_eq!(import.namespace_import, Some("math".to_string()));
    }

    #[test]
    fn test_default_import() {
        let result = parse_import(r#"import math from "./math.nr""#);
        assert!(result.is_ok());
        let import = result.unwrap();
        assert_eq!(import.path, "./math.nr");
        assert_eq!(import.default_import, Some("math".to_string()));
    }

    #[test]
    fn test_from_namespace_import() {
        let result = parse_import(r#"from "./math.nr" import * as math"#);
        assert!(result.is_ok());
        let import = result.unwrap();
        assert_eq!(import.namespace_import, Some("math".to_string()));
    }

    #[test]
    fn test_multiple_aliases_without_braces() {
        let result = parse_import(r#"import add as plus, subtract as minus from "./math.nr""#);
        assert!(result.is_ok());
        let import = result.unwrap();
        assert_eq!(import.named_imports.len(), 2);
        assert_eq!(import.named_imports[0].name, "add");
        assert_eq!(import.named_imports[0].alias, Some("plus".to_string()));
        assert_eq!(import.named_imports[1].name, "subtract");
        assert_eq!(import.named_imports[1].alias, Some("minus".to_string()));
    }

    #[test]
    fn test_mixed_with_and_without_alias() {
        let result = parse_import(r#"import add, subtract as minus from "./math.nr""#);
        assert!(result.is_ok());
        let import = result.unwrap();
        assert_eq!(import.named_imports.len(), 2);
        assert_eq!(import.named_imports[0].name, "add");
        assert_eq!(import.named_imports[0].alias, None);
        assert_eq!(import.named_imports[1].name, "subtract");
        assert_eq!(import.named_imports[1].alias, Some("minus".to_string()));
    }
}
